﻿using System;
using System.Threading;
using System.Threading.Tasks;
using MediatR;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using SW.DDD.Infrastructure;

namespace SW.DDD.Application.CommandPipelineBehaviors
{
    /// <summary>
    /// 命令管道事务
    /// </summary>
    /// <typeparam name="TCommand"></typeparam>
    /// <typeparam name="TResponse"></typeparam>
    public class CommandPipelineTransaction<TCommand, TResponse> : IPipelineBehavior<TCommand, TResponse>
        where TCommand : notnull
    {
        private readonly IUnitOfWork _unitOfWork;

        public CommandPipelineTransaction(IUnitOfWork unitOfWork, ILogger<CommandPipelineTransaction<TCommand, TResponse>>? logger = null)
        {
            _unitOfWork = unitOfWork;
            Logger ??= NullLogger<CommandPipelineTransaction<TCommand, TResponse>>.Instance;
        }

        public ILogger<CommandPipelineTransaction<TCommand, TResponse>> Logger { get; }

        public async Task<TResponse> Handle(TCommand cmd, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
        {
            if(!typeof(ICommand).IsAssignableFrom(typeof(TCommand)))
                return await next();

            if(_unitOfWork.GetCurrentTransactionId() != null)
                return await next();

            await _unitOfWork.BeginTransactionAsync(cancellationToken);
            using var loggerScope = Logger.BeginScope("Transaction Begin，ID:{}", _unitOfWork.GetCurrentTransactionId());

            try
            {
                var response = await next();
                await _unitOfWork.CommitTransactionAsync(cancellationToken);
                return response;
            }
            catch
            {
                Logger.LogError("Transaction Failed，ID:{}", _unitOfWork.GetCurrentTransactionId());
                await _unitOfWork.RollbackTransactionAsync();
                throw;
            }
        }
    }
}
